#include <wx/wx.h>
#include <iostream>
#include <string>
#include <stack>
#include <cctype>
#include <stdexcept>
#include <sstream>

class GridSizer : public wxFrame
{
public:
    GridSizer(const wxString &title);

    wxMenuBar *menubar;
    wxMenu *file;

private:
    void OnClose(wxCloseEvent &event);

    bool isDigitOrDot(char c)
    {
        return std::isdigit(c) || c == '.';
    }

    int precedence(char op) const
    {
        static const std::unordered_map<char, int> precedenceMap = {
            {'+', 1},
            {'-', 1},
            {'*', 2},
            {'/', 2}};
        auto it = precedenceMap.find(op);
        if (it == precedenceMap.end())
            throw std::runtime_error("Invalid operator");
        return it->second;
    }

    double applyOperation(char op, double operand2, double operand1)
    {
        switch (op)
        {
        case '+':
            return operand1 + operand2;
        case '-':
            return operand1 - operand2;
        case '*':
            return operand1 * operand2;
        case '/':
            if (operand2 == 0.0 || std::isinf(operand2) || std::isinf(operand1))
                throw std::runtime_error("Division by zero or overflow");
            return operand1 / operand2;
        default:
            throw std::runtime_error("Unsupported operation");
        }
    }

    double parseDouble(const std::string &str, size_t &i)
    {
        size_t j = i;
        while (j < str.length() && isDigitOrDot(str[j]))
            ++j;
        if (j == i)
            throw std::runtime_error("No valid number found"); // 验证失败
        std::istringstream iss(str.substr(i, j - i));
        double value;
        if (!(iss >> value))
            throw std::runtime_error("Invalid number format"); // 验证失败
        i = j;
        return value;
    }

    double evaluateExpression(const std::string &expr)
    {
        if (expr.empty())
            throw std::runtime_error("Empty expression"); // 表达式验证
        std::stack<double> values;
        std::stack<char> ops;

        for (size_t i = 0; i < expr.length(); ++i)
        {
            if (isDigitOrDot(expr[i]))
            {
                values.push(parseDouble(expr, i));
            }
            else if (expr[i] == ' ')
            {
                continue;
            }
            else if (expr[i] == '+' || expr[i] == '-' || expr[i] == '*' || expr[i] == '/')
            {
                while (!ops.empty() && precedence(ops.top()) >= precedence(expr[i]))
                {
                    double operand2 = values.top();
                    values.pop();
                    double operand1 = values.top();
                    values.pop();
                    char op = ops.top();
                    ops.pop();
                    values.push(applyOperation(op, operand2, operand1));
                }
                ops.push(expr[i]);
            }
            else
            {
                throw std::runtime_error("Invalid character in expression");
            }
        }

        while (!ops.empty())
        {
            double operand2 = values.top();
            values.pop();
            double operand1 = values.top();
            values.pop();
            char op = ops.top();
            ops.pop();
            values.push(applyOperation(op, operand2, operand1));
        }

        if (values.size() != 1)
            throw std::runtime_error("Invalid expression"); // 额外的验证
        return values.top();
    }

    double compute(wxString &text)
    {
        try
        {
            return evaluateExpression(text.ToStdString());
        }
        catch (const std::exception &e)
        {
            wxMessageBox(wxString::Format("Error: %s", e.what()), "Error", wxOK | wxICON_ERROR);
            return 0.0;
        }
    }

private:
    wxBoxSizer *sizer;
    wxGridSizer *gs;
    wxTextCtrl *display;
};

GridSizer::GridSizer(const wxString &title)
    : wxFrame(NULL, -1, title, wxPoint(-1, -1), wxSize(270, 220))
{
    menubar = new wxMenuBar;
    file = new wxMenu;

    SetMenuBar(menubar);

    sizer = new wxBoxSizer(wxVERTICAL);

    display = new wxTextCtrl(this, -1, wxT(""), wxPoint(-1, -1),
                             wxSize(-1, -1), wxTE_RIGHT);

    sizer->Add(display, 0, wxEXPAND | wxTOP | wxBOTTOM, 4);
    gs = new wxGridSizer(5, 4, 3, 3);

    gs->Add(new wxButton(this, -1, wxT("Cls")), 0, wxEXPAND);
    gs->Add(new wxButton(this, -1, wxT("Bck")), 0, wxEXPAND);
    gs->Add(new wxStaticText(this, -1, wxT("")), 0, wxEXPAND);
    gs->Add(new wxButton(this, -1, wxT("Close")), 0, wxEXPAND);
    gs->Add(new wxButton(this, -1, wxT("7")), 0, wxEXPAND);
    gs->Add(new wxButton(this, -1, wxT("8")), 0, wxEXPAND);
    gs->Add(new wxButton(this, -1, wxT("9")), 0, wxEXPAND);
    gs->Add(new wxButton(this, -1, wxT("/")), 0, wxEXPAND);
    gs->Add(new wxButton(this, -1, wxT("4")), 0, wxEXPAND);
    gs->Add(new wxButton(this, -1, wxT("5")), 0, wxEXPAND);
    gs->Add(new wxButton(this, -1, wxT("6")), 0, wxEXPAND);
    gs->Add(new wxButton(this, -1, wxT("*")), 0, wxEXPAND);
    gs->Add(new wxButton(this, -1, wxT("1")), 0, wxEXPAND);
    gs->Add(new wxButton(this, -1, wxT("2")), 0, wxEXPAND);
    gs->Add(new wxButton(this, -1, wxT("3")), 0, wxEXPAND);
    gs->Add(new wxButton(this, -1, wxT("-")), 0, wxEXPAND);
    gs->Add(new wxButton(this, -1, wxT("0")), 0, wxEXPAND);
    gs->Add(new wxButton(this, -1, wxT(".")), 0, wxEXPAND);
    gs->Add(new wxButton(this, -1, wxT("=")), 0, wxEXPAND);
    gs->Add(new wxButton(this, -1, wxT("+")), 0, wxEXPAND);

    Bind(wxEVT_BUTTON, [&](wxCommandEvent &evt)
         {
        wxObject *sender = evt.GetEventObject();
        wxButton* clickedButton = dynamic_cast<wxButton*>(sender);

        auto text = clickedButton->GetLabelText();
        if(text.IsNumber() || text == "." || text == "+" || text == "-" || text == "*" || text == "/")
            display->AppendText(clickedButton->GetLabelText());
        else if(text == "=")
        {
            auto value = compute(display->GetValue());
            display->Clear();
            display->AppendText(wxString::Format("%.2f", value));
        }
        else if(text == "Cls")
            display->Clear();
        else if(text == "Bck")
            display->Remove(display->GetLastPosition(), display->GetLastPosition()-1); }, wxID_ANY);

    Bind(wxEVT_CLOSE_WINDOW, &GridSizer::OnClose, this);

    sizer->Add(gs, 1, wxEXPAND);
    SetSizer(sizer);
    SetMinSize(wxSize(270, 220));

    Centre();
}

void GridSizer::OnClose(wxCloseEvent &event)
{
    Close(true);
}

class MyApp : public wxApp
{
public:
    virtual bool OnInit();
};

IMPLEMENT_APP(MyApp)

bool MyApp::OnInit()
{

    GridSizer *gs = new GridSizer(wxT("GridSizer"));
    gs->Show(true);

    return true;
}